﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using Xceed.Wpf.Toolkit;
using System.Reflection;
using Microsoft.Xna.Framework;
using System.Windows.Controls.Primitives;
using System.Diagnostics;
using Skugo.Shared;

namespace Skugo.StandardPlugins
{
    [DefaultEditorForType(Type = typeof(String))]
    public class StringEditor : ITypeEditor
    {
        private TextBox Box = new TextBox();

        private Boolean IgnoreValueChanges;

        public StringEditor(Type forType)
        {
            this.Box.GotFocus += this.GotFocus;
            this.Box.LostFocus += this.OnLostFocus;
            this.Box.TextChanged += this.OnTextChanged;
        }

        void GotFocus(object sender, RoutedEventArgs e)
        {
            this.Box.SelectAll();
        }

        void OnTextChanged(object sender, TextChangedEventArgs e)
        {
            if (this.IgnoreValueChanges == false && this.OnPropertyEdited != null)
            {
                this.OnPropertyEdited(this.Box.Text);
            }
        }

        void OnLostFocus(object sender, RoutedEventArgs e)
        {
            this.OnTextChanged(null, null);
        }

        public void SetValue(object value)
        {
            this.IgnoreValueChanges = true;

            if (value != null)
            {
                var result = (String)value;
                if (result != this.Box.Text)
                {
                    this.Box.Text = result;
                }
            }
            else
            {
                this.Box.Text = null;
            }

            this.IgnoreValueChanges = false;
        }

        public Boolean IsEditing
        {
            get { return this.Box.IsFocused; }
        }

        public event PropertyEditedFn OnPropertyEdited;

        public FrameworkElement Content
        {
            get { return this.Box; }
        }

        public bool IsLarge { get { return false; } }
    }

    [DefaultEditorForType(Type = typeof(Byte))]
    [DefaultEditorForType(Type = typeof(UInt16))]
    [DefaultEditorForType(Type = typeof(UInt32))]
    [DefaultEditorForType(Type = typeof(UInt64))]
    [DefaultEditorForType(Type = typeof(SByte))]
    [DefaultEditorForType(Type = typeof(Int16))]
    [DefaultEditorForType(Type = typeof(Int32))]
    [DefaultEditorForType(Type = typeof(Int64))]
    public class IntegerEditor : ITypeEditor
    {
        private IntegerUpDown Box = new IntegerUpDown();

        private Boolean IgnoreValueChanges;

        private Type ForType;

        public IntegerEditor(Type forType)
        {
            this.ForType = forType;

            var maxValue = Convert.ToUInt64(forType.GetField("MaxValue").GetValue(null));
            if (maxValue > Int32.MaxValue)
            {
                maxValue = Int32.MaxValue;
            }
            var minValue = Convert.ToInt64(forType.GetField("MinValue").GetValue(null));

            if (minValue < Int32.MinValue)
            {
                minValue = Int32.MinValue;
            }

            this.Box.Maximum = (Int32)maxValue;
            this.Box.Minimum = (Int32)minValue;

            this.Box.SelectAllOnGotFocus = true;
            this.Box.TextAlignment = TextAlignment.Left;
            this.Box.GotFocus += this.OnGotFocus;
            this.Box.LostFocus += this.OnLostFocus;
            this.Box.ValueChanged += this.OnValueChanged;
        }

        void OnValueChanged(Object sender, RoutedPropertyChangedEventArgs<Object> e)
        {
            if (this.IgnoreValueChanges == false && this.OnPropertyEdited != null)
            {
                var value = Convert.ChangeType(this.Box.Value, this.ForType);
                this.OnPropertyEdited(value);
            }
        }

        void OnGotFocus(object sender, RoutedEventArgs e)
        {
            this.IsEditing = true;
        }

        void OnLostFocus(Object sender, RoutedEventArgs e)
        {
            this.OnValueChanged(null, null);
        }

        public void SetValue(object value)
        {
            this.IgnoreValueChanges = true;

            if (value != null)
            {
                var result = Convert.ToInt32(value);
                if (this.Box.Value != result)
                {
                    this.Box.Value = result;
                }
            }
            else
            {
                this.Box.Value = null;
            }

            this.IgnoreValueChanges = false;
        }

        public Boolean IsEditing { get; set; }

        public event PropertyEditedFn OnPropertyEdited;

        public FrameworkElement Content
        {
            get { return this.Box; }
        }

        public bool IsLarge { get { return false; } }
    }

    [DefaultEditorForType(Type = typeof(Single))]
    [DefaultEditorForType(Type = typeof(Double))]
    public class FloatEditor : ITypeEditor
    {
        private DoubleUpDown Box = new DoubleUpDown();
        private Boolean IgnoreValueChanges;
        private Type ForType;

        public FloatEditor(Type forType)
        {
            this.ForType = forType;

            if (forType == typeof(Single))
            {
                this.Box.Maximum = Single.MaxValue;
                this.Box.Minimum = Single.MinValue;
            }
            else
            {
                this.Box.Maximum = Double.MaxValue;
                this.Box.Minimum = Double.MinValue;
            }

            this.Box.FormatString = "F";
            this.Box.SelectAllOnGotFocus = true;
            this.Box.TextAlignment = TextAlignment.Left;
            this.Box.GotFocus += this.OnGotFocus;
            this.Box.LostFocus += this.OnLostFocus;
            this.Box.ValueChanged += this.OnValueChanged;
        }

        void OnValueChanged(Object sender, RoutedPropertyChangedEventArgs<Object> e)
        {
            if (this.IgnoreValueChanges == false && this.OnPropertyEdited != null)
            {
                var value = Convert.ChangeType(this.Box.Value, this.ForType);
                this.OnPropertyEdited(value);
            }
        }

        void OnGotFocus(object sender, RoutedEventArgs e)
        {
            this.IsEditing = true;
        }

        void OnLostFocus(Object sender, RoutedEventArgs e)
        {
            this.IsEditing = false;
            this.OnValueChanged(null, null);
        }

        public void SetValue(object value)
        {
            this.IgnoreValueChanges = true;

            if (value != null)
            {
                var result = Convert.ToDouble(value);

                if (this.Box.Value != result)
                {
                    this.Box.Value = result;
                }
            }
            else
            {
                this.Box.Value = null;
            }

            this.IgnoreValueChanges = false;
        }

        public Boolean IsEditing { get; set; }

        public event PropertyEditedFn OnPropertyEdited;

        public FrameworkElement Content
        {
            get { return this.Box; }
        }

        public bool IsLarge { get { return false; } }
    }


    [DefaultEditorForType(Type = typeof(Boolean))]
    public class BoolEditor : ITypeEditor
    {
        private CheckBox Box = new CheckBox();
        private Boolean IgnoreValueChanges;

        public BoolEditor(Type forType)
        {
            Debug.Assert(forType == typeof(Boolean));

            this.Box.Checked += this.OnValueChanged;
            this.Box.Unchecked += this.OnValueChanged;
        }

        void OnValueChanged(object sender, RoutedEventArgs e)
        {
            if (this.IgnoreValueChanges == false && this.OnPropertyEdited != null)
            {
                var value = this.Box.IsChecked;
                this.OnPropertyEdited(value);
            }
        }

        public void SetValue(object value)
        {
            this.IgnoreValueChanges = true;

            if (value != null)
            {
                var result = (Boolean)value;

                this.Box.IsChecked = result;
            }
            else
            {
                this.Box.IsChecked = false;
            }

            this.IgnoreValueChanges = false;
        }

        // Bools are only checkboxes and don't really get focus at all
        public Boolean IsEditing { get { return false; } }

        public event PropertyEditedFn OnPropertyEdited;

        public FrameworkElement Content
        {
            get { return this.Box; }
        }

        public bool IsLarge { get { return false; } }
    }


    [DefaultEditorForType(Type = typeof(Color))]
    public class ColorEditor : ITypeEditor
    {
        private ColorPicker Picker = new ColorPicker();
        private Boolean IgnoreValueChanges;

        public ColorEditor(Type forType)
        {
            Debug.Assert(forType == typeof(Color));

            this.Picker.DisplayColorAndName = true;
            this.Picker.SelectedColorChanged += this.OnValueChanged;
        }

        void OnValueChanged(object sender, RoutedPropertyChangedEventArgs<System.Windows.Media.Color> e)
        {
            if (this.IgnoreValueChanges == false && this.OnPropertyEdited != null)
            {
                var value = new Color(e.NewValue.R, e.NewValue.G, e.NewValue.B, e.NewValue.A);
                this.OnPropertyEdited(value);
            }
        }

        public void SetValue(object value)
        {
            this.IgnoreValueChanges = true;

            if (value != null)
            {
                var result = (Color)value;

                this.Picker.SelectedColor = System.Windows.Media.Color.FromArgb(result.A, result.R, result.G, result.B);
            }
            else
            {
                this.Picker.SelectedColor = System.Windows.Media.Colors.White;
            }

            this.IgnoreValueChanges = false;
        }

        // Bools are only checkboxes and don't really get focus at all
        public Boolean IsEditing { get { return this.Picker.IsOpen || this.Picker.IsFocused; } }

        public event PropertyEditedFn OnPropertyEdited;

        public FrameworkElement Content
        {
            get { return this.Picker; }
        }

        public bool IsLarge { get { return false; } }
    }

    [DefaultEditorForType(Type = typeof(Vector2))]
    public class Vector2Editor : ITypeEditor
    {
        private DoubleUpDown X = new DoubleUpDown();
        private DoubleUpDown Y = new DoubleUpDown();

        private Boolean IgnoreValueChanges;

        private UniformGrid Horizontal = new UniformGrid();

        public Vector2Editor(Type forType)
        {
            this.X.FormatString = "F";
            this.X.SelectAllOnGotFocus = true;
            this.X.TextAlignment = TextAlignment.Left;
            this.X.GotFocus += this.OnGotFocus;
            this.X.LostFocus += this.OnLostFocus;
            this.X.ValueChanged += this.OnValueChanged;
            this.X.Margin = new Thickness(0, 0, 2, 0);

            this.Y.FormatString = "F";
            this.Y.SelectAllOnGotFocus = true;
            this.Y.TextAlignment = TextAlignment.Left;
            this.Y.GotFocus += this.OnGotFocus;
            this.Y.LostFocus += this.OnLostFocus;
            this.Y.ValueChanged += this.OnValueChanged;
            this.Y.Margin = new Thickness(2, 0, 0, 0);

            this.Horizontal.Rows = 1;
            this.Horizontal.Columns = 2;

            this.Horizontal.Children.Add(this.X);
            this.Horizontal.Children.Add(this.Y);
        }

        void OnValueChanged(Object sender, RoutedPropertyChangedEventArgs<Object> e)
        {
            if (this.IgnoreValueChanges == false && this.OnPropertyEdited != null)
            {
                var value = new Vector2((Single)this.X.Value, (Single)this.Y.Value);
                this.OnPropertyEdited(value);
            }
        }

        void OnGotFocus(object sender, RoutedEventArgs e)
        {
            this.IsEditing = true;
        }

        void OnLostFocus(Object sender, RoutedEventArgs e)
        {
            this.IsEditing = false;
            this.OnValueChanged(null, null);
        }

        public void SetValue(object value)
        {
            this.IgnoreValueChanges = true;

            if (value != null)
            {
                var result = (Vector2)value;

                if (this.X.Value != result.X)
                {
                    this.X.Value = result.X;
                }

                if (this.Y.Value != result.Y)
                {
                    this.Y.Value = result.Y;
                }
            }
            else
            {
                this.X.Value = null;
                this.Y.Value = null;
            }

            this.IgnoreValueChanges = false;
        }

        public Boolean IsEditing { get; set; }

        public event PropertyEditedFn OnPropertyEdited;

        public FrameworkElement Content
        {
            get { return this.Horizontal; }
        }

        public bool IsLarge { get { return true; } }
    }

    /// <summary>
    /// The enumerable editor is a special editor that we instantiate for any enumerable object.
    /// </summary>
    public class EnumerableEditor : ITypeEditor
    {
        private ComboBox Combo = new ComboBox();

        private Boolean IgnoreValueChanges;

        public EnumerableEditor(String[] options)
        {
            this.Combo.ItemsSource = options;
            this.Combo.SelectionChanged += this.OnSelectionChanged;
        }

        void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (this.IgnoreValueChanges == false && this.OnPropertyEdited != null)
            {
                this.OnPropertyEdited(this.Combo.SelectedIndex);
            }
        }

        public void SetValue(Object value)
        {
            this.IgnoreValueChanges = true;

            if (value != null)
            {
                // Get the index into the combo box
                this.Combo.SelectedIndex = (Int32)value;
            }

            this.IgnoreValueChanges = false;
        }

        public Boolean IsEditing
        {
            get { return this.Combo.IsFocused; }
        }

        public event PropertyEditedFn OnPropertyEdited;

        public FrameworkElement Content
        {
            get { return this.Combo; }
        }

        public bool IsLarge { get { return false; } }
    }
}
